package yl.hs.bmipfull.service.finance.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import yl.hs.bmipfull.daos.finance.IReceiptSettleDetailDao;
import yl.hs.bmipfull.daos.finance.IReceiptSettleMainDao;
import yl.hs.bmipfull.daos.finance.ISettleAdditionDao;
import yl.hs.bmipfull.daos.finance.ISettleOrderDao;
import yl.hs.bmipfull.daos.material.IPurchaseContractDao;
import yl.hs.bmipfull.daos.material.IPurchaseReceiptDao;
import yl.hs.bmipfull.pojo.finance.*;
import yl.hs.bmipfull.pojo.material.PurchaseReceiptEntity;
import yl.hs.bmipfull.service.finance.IReceiptSettleService;
import yl.hs.bmipfull.utils.SearchFilterEngine;
import yl.hs.bmipfull.utils.Tl;
import yl.hs.bmipfull.utils.datastate.ActionType;
import yl.hs.bmipfull.viewmodels.PageQuery;
import yl.hs.bmipfull.viewmodels.PageResult;
import yl.hs.bmipfull.viewmodels.SubmitModel;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class ReceiptSettleServiceImpl extends FinanceService implements IReceiptSettleService {

    @Override
    public PageResult<Map<String, Object>> queryPage(PageQuery pageQuery, int rowNum, int page) throws ParseException {
        PageResult<Map<String,Object>>  result;
        pageQuery.checkSortSides(PurchaseReceiptEntity.class);
        var filters = SearchFilterEngine.generateQueryList(pageQuery.getFilters(), PurchaseReceiptEntity.class);
        List<Map<String,Object>> data = new ArrayList<>();
        if(rowNum>0){
            result = new PageResult<>(rowNum);
            pageQuery.getQuery().put("querySkip", (page - 1) * rowNum);
            pageQuery.getQuery().put("pageSize", rowNum);
            result.setRecords(mainDao.queryDataCountByPage(pageQuery.getQuery(),filters));
            result.setPage(page);
            var list = mainDao.queryPage(pageQuery.getQuery(),filters);
            for (ReceiptSettleMainEntity item : list) {
                data.add(Tl.toMap(item));
            }
        }
        else
        {
            var searchQuery = pageQuery.getQuery();
            var defFilters = pageQuery.getFilters();
            if(defFilters!=null && defFilters.size()>0){
                var startTimeOpt = defFilters.stream().filter(t->t.getField().equals("startTime")).findFirst();
                var endTimeOpt = defFilters.stream().filter(t->t.getField().equals("endTime")).findFirst();
                startTimeOpt.ifPresent(queryFilter -> searchQuery.put("startTime", queryFilter.getSearchVal()));
                endTimeOpt.ifPresent(queryFilter -> searchQuery.put("endTime", queryFilter.getSearchVal()));
            }
            var orderList = mainDao.queryTempSettleOrders(pageQuery.getQuery(),filters);
            var contractList = mainDao.querySettleContract(pageQuery.getQuery(),filters);
            var contractLastSettle = mainDao.queryContractSettleLast(pageQuery.getQuery(),filters);
            var settlePayedList = mainDao.queryContractPayedAmount(pageQuery.getQuery(),filters);
            result = new PageResult<>(contractList.size()>0?contractList.size():1);
            result.setRecords(contractList.size());
            result.setPage(page);
            for (yl.hs.bmipfull.pojo.material.PurchaseContractEntity purchaseContractEntity : contractList) {
                ReceiptSettleMainEntity item = new ReceiptSettleMainEntity();
                item.setSn(purchaseContractEntity.getSn());
                var orders = orderList.stream().filter(t -> purchaseContractEntity.getSn().equals(t.getContractSN())).collect(Collectors.toList());
                item.setContractRemark(purchaseContractEntity.getRemark());
                item.setContractSN(purchaseContractEntity.getSn());
                item.setSupplierName(purchaseContractEntity.getSupplierName());
                item.setSupplierSN(purchaseContractEntity.getSupplierSN());
                item.setSettleTime(new Date());
                Date min, max;
                var searchStart = pageQuery.getQuery().get("startTime");
                var searchEnd = pageQuery.getQuery().get("endTime");
                if (searchStart == null || "".equals(searchStart)) {
                    var minOpt = orders.stream().map(PurchaseReceiptEntity::getOutTime).min(Date::compareTo);
                    min = minOpt.orElseGet(Date::new);
                } else {
                    min = Tl.strToDate(searchStart.toString());
                }
                if (searchEnd == null || "".equals(searchEnd)) {
                    var maxOpt = orders.stream().map(PurchaseReceiptEntity::getOutTime).max(Date::compareTo);
                    max = maxOpt.orElseGet(Date::new);
                } else {
                    max = Tl.strToDate(searchEnd.toString());
                }
                item.setStartTime(min);
                item.setEndTime(max);
                var lastSettle = contractLastSettle.stream().filter(t -> t.getContractSN().equals(purchaseContractEntity.getSn())).findFirst();
                BigDecimal previousAmount = BigDecimal.valueOf(0.0),
                        totalPayedAmount = BigDecimal.valueOf(0.0), currentPayedAmount = BigDecimal.valueOf(0.0),
                        totalQuantity = BigDecimal.valueOf(0.0), currentQuantity = BigDecimal.valueOf(0.0),
                        totalAmount = BigDecimal.valueOf(0.0), currentAmount = BigDecimal.valueOf(0.0),
                        unAQuantity = BigDecimal.valueOf(0.0), unAAmount = BigDecimal.valueOf(0.0),//已审核未结算
                        unQuantity = BigDecimal.valueOf(0.0), unAmount = BigDecimal.valueOf(0.0);//未审核未结算
                int totalOrderCount = 0, currentOrderCount = 0, unAOrderCount = 0, unOrderCount = 0;
                item.setSettleWay("周期");
                if (lastSettle.isPresent()) {
                    var last = lastSettle.get();
                    previousAmount = last.getCurrentAmount();
                    totalPayedAmount = last.getTotalPayedAmount();
                    totalQuantity = last.getTotalQuantity();
                    totalAmount = last.getTotalAmount();
                    totalOrderCount = last.getTotalOrderCount();
                } else {
                    if (purchaseContractEntity.getInitialAmount() != null) {
                        totalAmount = totalAmount.add(purchaseContractEntity.getInitialAmount());
                    }
                }
                for (PurchaseReceiptEntity order : orders) {
                    var price = order.getPrice();

                    BigDecimal orderWeight;
                    if (order.getState() == 10) {//已审核
                        orderWeight = order.getReviewWeight() == null ? BigDecimal.ZERO : order.getReviewWeight();
                        if (order.getOutTime().compareTo(max) <= 0) {//参与结算
                            currentQuantity = currentQuantity.add(orderWeight);
                            currentAmount = currentAmount.add((orderWeight).multiply(price));
                            currentOrderCount++;
                        } else //不参与结算
                        {
                            unAQuantity = unAQuantity.add(orderWeight);
                            unAAmount = unAAmount.add((orderWeight).multiply(price));
                            unAOrderCount++;
                        }
                    } else if (order.getState() == 1) {
                        orderWeight = order.getNetWeight();
                        unQuantity = unQuantity.add(orderWeight);
                        unAmount = unAmount.add((orderWeight).multiply(price));
                        unOrderCount++;
                    }
                }
                var contractReceiptAmountObj = settlePayedList.stream().filter(t -> purchaseContractEntity.getSn().equals(t.getSn())).findFirst();
                if (contractReceiptAmountObj.isPresent()) {
                    currentPayedAmount = contractReceiptAmountObj.get().getAmount();
                    totalPayedAmount = totalPayedAmount.add(currentPayedAmount);
                }
                item.setCurrentQuantity(currentQuantity);
                item.setTotalQuantity(totalQuantity.add(currentQuantity));
                item.setPreviousAmount(previousAmount);
                item.setCurrentAmount(currentAmount);
                item.setTotalAmount(totalAmount.add(currentAmount));
                item.setTotalPayedAmount(totalPayedAmount);
                item.setCurrentPayedAmount(currentPayedAmount);
                item.setCurrentOrderCount(currentOrderCount);
                item.setTotalOrderCount(totalOrderCount);
                item.setRemark("");
                item.setState((short) 1);
                item.setTags("");
                var dataMap = Tl.toMap(item);
                dataMap.put("unSettleApprovedQuantity", unAQuantity);
                dataMap.put("unSettleApprovedAmount", unAAmount);
                dataMap.put("unSettleApprovedOrderCount", unAOrderCount);
                dataMap.put("unApprovedQuantity", unQuantity);
                dataMap.put("unApprovedAmount", unAmount);
                dataMap.put("unApprovedOrderCount", unOrderCount);
                data.add(dataMap);
            }
        }
        result.setData(data);
        return result;
    }

    @Override
    public Map<String,Object> querySettleDetailsBySettleSN(String settleSN){
        Map<String,Object> result = new HashMap<>();
        result.put("details", detailDao.queryBySettleSN(settleSN));
        result.put("additions",additionDao.queryAdditionsBySettleSN(settleSN));
        return result;
    }

    @Override
    public List<Map<String,Object>> queryTempSettleDetails(String contractSN, String companySN, String startDate, String endDate) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date start = null,end=null;
        if(!Tl.isEmpty(startDate)) {
            start = sdf.parse(startDate);
        }
        if(!Tl.isEmpty(endDate)) {
            end = sdf.parse(endDate);
            Calendar   calendar = new GregorianCalendar();
            calendar.setTime(end);
            calendar.add(Calendar.SECOND,2);
            end = calendar.getTime();
        }
        var data = detailDao.queryTempDetails(contractSN,companySN,start,end);
        for (var o:data){
            if(o.getRmCateSN()==null){
                o.setRmCateSN("");
            }
            if(o.getRmCate() ==null){
                o.setRmCate("");
            }
            if(o.getRmName()==null){
                o.setRmName("");
            }
            if(o.getRmSN()==null){
                o.setRmSN("");
            }
            if(o.getRmSpec()==null){
                o.setRmSpec("");
            }
            if(o.getBrand()==null){
                o.setBrand("");
            }
            if(o.getPrice()==null){
                o.setPrice(BigDecimal.ZERO);
            }
        }
       var groupData = data.stream().collect(
               Collectors.groupingBy(ReceiptSettleTemp::getRmCateSN,
                Collectors.groupingBy(ReceiptSettleTemp::getRmCate,
                        Collectors.groupingBy(ReceiptSettleTemp::getRmSN,
                                Collectors.groupingBy(ReceiptSettleTemp::getRmName,
                                        Collectors.groupingBy(ReceiptSettleTemp::getRmSpec,
                                                Collectors.groupingBy(ReceiptSettleTemp::getBrand,
                                                        Collectors.groupingBy(ReceiptSettleTemp::getPrice,
                                                                Collectors.toList()))))))));
        List<Map<String,Object>> list = new ArrayList<>();
       for (var cateSN:groupData.keySet()){
           var cateSNGroup = groupData.get(cateSN);
           for (var rmCate: cateSNGroup.keySet()){
               var rmCateGroup = cateSNGroup.get(rmCate);
               for(var rmSN: rmCateGroup.keySet()){
                   var rmSNGroup = rmCateGroup.get(rmSN);
                   for(var rmName: rmSNGroup.keySet()){
                       var rmNameGroup = rmSNGroup.get(rmName);
                       for(var rmSpec: rmNameGroup.keySet()){
                           var rmSpecGroup = rmNameGroup.get(rmSpec);
                           for(var rmBrand: rmSpecGroup.keySet()){
                               var rmBrandGroup = rmSpecGroup.get(rmBrand);
                               for(var price: rmBrandGroup.keySet()){
                                   var priceList = rmBrandGroup.get(price);

                                   var st = new ReceiptSettleDetailEntity();
                                   var  dayFormat = new SimpleDateFormat("yyyy-MM-dd");
                                   String minTime="",maxTime="";
                                   var minDate = priceList.stream().min(Comparator.comparing(ReceiptSettleTemp::getOutTime));
                                   if(minDate.isPresent()){
                                       minTime = dayFormat.format( minDate.get().getOutTime());
                                   }
                                   var maxDate = priceList.stream().max(Comparator.comparing(ReceiptSettleTemp::getOutTime));
                                   if(maxDate.isPresent()){
                                       maxTime = dayFormat.format( maxDate.get().getOutTime());
                                   }
                                    st.setTitle(String.format("%s 至 %s",minTime,maxTime));
                                   st.setRmCateSN(cateSN);
                                   st.setRmCateName(rmCate);
                                   st.setRmSN(rmSN);
                                   st.setRmName(rmName);
                                   st.setRmSpec(rmSpec);
                                   st.setRmBrand(rmBrand);
                                   st.setPrice(price);
                                  var totalQuantity = priceList.stream().map(ReceiptSettleTemp::getReviewQuantity).reduce(BigDecimal.ZERO,BigDecimal::add);
                                   var totalWeight = priceList.stream().map(ReceiptSettleTemp::getReviewWeight).reduce(BigDecimal.ZERO,BigDecimal::add);
                                   st.setTotalQuantity(totalQuantity);
                                   st.setTotalWeight(totalWeight);
                                   st.setOrderCount(priceList.size());
                                   st.setAmount(totalWeight.multiply(price));
                                   var map = st.toMap();
                                   map.put("orderSnArray",priceList.stream().map(ReceiptSettleTemp::getSn).toArray());
                                   list.add(map);
                               }
                           }
                       }
                   }
               }
           }
       }
        return  list;
    }

    @Override
    public Integer cancelSettle(String settleSN){
        var result = mainDao.deleteBySettleSN(settleSN);
        if(result>0){
            detailDao.deleteBySettleSN(settleSN);
            additionDao.deleteBySettleSN(settleSN);
            var orderSNList  = orderDao.queryShipOrderSNListBySettleSN(settleSN);
            mainDao.changeOrderToUnSettle(orderSNList);
            orderDao.deleteBySettleSN(settleSN);
        }
        return result;
    }

    @Override
    public int updateExportCount(String settleSN) {
        int result=0;
        var entity=mainDao.findBySN(settleSN);
        if(entity!=null){
            entity.setExportCount(entity.getExportCount()+1);
            result=mainDao.update(entity);
        }
        return result;
    }

    @Override
    public Integer saveSettle(SubmitModel model) throws Exception {
        var result = 0;
        var settleMainInfo = new ReceiptSettleMainEntity();
        Tl.getChangeLog(settleMainInfo,model.getData());
        var settleSN = queryNewSN(settleMainInfo.getClass());
        settleMainInfo.setSn(settleSN);
        settleMainInfo.setState((short) 1);
        settleMainInfo.setSettleTime(new Date());
        settleMainInfo.setPYFieldValue();
        settleMainInfo.setOrganizationCode(getDepartSN());
        if(!Tl.isEmpty(settleMainInfo.getContractSN())){
            var contract=contractDao.findBySN(settleMainInfo.getContractSN());
            settleMainInfo.setContractingUnit(contract.getContractingUnit());
        }
        var lastSettle = mainDao.getLastSettle(settleMainInfo.getContractSN());
        if(lastSettle!=null) {
            settleMainInfo.setPreviousAmount(lastSettle.getCurrentAmount());
            settleMainInfo.setTotalAmount(lastSettle.getTotalAmount().add(settleMainInfo.getCurrentAmount()));
            settleMainInfo.setTotalOrderCount(lastSettle.getTotalOrderCount() + settleMainInfo.getCurrentOrderCount());
            settleMainInfo.setTotalQuantity(lastSettle.getTotalQuantity().add(settleMainInfo.getCurrentQuantity()));
            settleMainInfo.setTotalPayedAmount(lastSettle.getTotalPayedAmount().add(settleMainInfo.getCurrentPayedAmount()));
        }
        else {
            settleMainInfo.setTotalAmount(settleMainInfo.getCurrentAmount());
            settleMainInfo.setTotalPayedAmount(settleMainInfo.getCurrentPayedAmount());
            settleMainInfo.setTotalQuantity(settleMainInfo.getCurrentQuantity());
            settleMainInfo.setTotalOrderCount(settleMainInfo.getCurrentOrderCount());
        }
        result = mainDao.insertNew(settleMainInfo);
        if(result>0) {
            var detailsObj = model.getData().get("details");
            if(detailsObj!=null){
                List<Map<String,Object>> detailList = (List<Map<String, Object>>) (detailsObj);
                if(detailList.size()>0){
                    List<SettleOrderEntity> relOrderList = new ArrayList<>();
                    var newSN = queryNewSN(ReceiptSettleDetailEntity.class);
                    List<String> allSNList =new ArrayList<>();
                    for (int i = 0; i < detailList.size(); i++) {
                        var detailMap = detailList.get(i);
                        var orderSnObj = detailMap.get("orderSnArray");
                        var detailItem = new ReceiptSettleDetailEntity();
                        var detailSN = new String(newSN);
                        if(i>0){
                            detailSN = queryNewSN(ReceiptSettleDetailEntity.class,newSN,i);
                        }

                        Tl.getChangeLog(detailItem,detailMap);
                        detailItem.setSn(detailSN);
                        detailItem.setSettleSN(settleSN);
                        var dResult = detailDao.insertNew(detailItem);
                        if(orderSnObj!=null){
                            List<String> orderSNList = (List<String>) orderSnObj;
                            if(orderSNList.size()>0){
                                allSNList.addAll(orderSNList);
                                for (int j = 0; j < orderSNList.size(); j++) {
                                    var settleOrder = new SettleOrderEntity();
                                    settleOrder.setSettleSN(settleSN);
                                    settleOrder.setDetailSN(detailSN);
                                    settleOrder.setOrderSN(orderSNList.get(j));
                                    orderDao.insertNew(settleOrder);
                                }
                                receiptDao.updateOrderPrice(detailItem.getPrice(), orderSNList);
                            }
                        }
                    }

                    mainDao.changeOrderToSettledForSNList(allSNList);
                }
            }
            var additionsObj = model.getData().get("additions");
            if (additionsObj!=null){
                List<Map<String,Object>> additionList = (List<Map<String, Object>>) additionsObj;
                if(additionList.size()>0){
                    for (int i = 0; i < additionList.size(); i++) {
                        var additionItem = new SettleAdditionEntity();
                        var additionMap = additionList.get(i);
                        additionItem.setSn(queryNewSN(additionItem.getClass()));
                        Tl.getChangeLog(additionItem,additionMap);
                        additionItem.setSettleSN(settleSN);
                        additionDao.insertNew(additionItem);
                    }
                }
            }
        }
        return  result;
    }

    @Override
    public List<PurchaseReceiptEntity> querySettleOrdersBySettleSN(String settleSN){
        return mainDao.querySettleOrdersBySettleSN(settleSN);
    }

    @Autowired
    public void setMainDao(IReceiptSettleMainDao mainDao) {
        this.mainDao = mainDao;
    }
    @Autowired
    public void setDetailDao(IReceiptSettleDetailDao detailDao) {
        this.detailDao = detailDao;
    }
    @Autowired
    public void setAdditionDao(ISettleAdditionDao additionDao) {
        this.additionDao = additionDao;
    }
    @Autowired
    public void setOrderDao(ISettleOrderDao orderDao) {
        this.orderDao = orderDao;
    }
    @Autowired
    public void setContractDao(IPurchaseContractDao contractDao) {
        this.contractDao = contractDao;
    }

    private IReceiptSettleMainDao mainDao;
    private IReceiptSettleDetailDao detailDao;
    private ISettleAdditionDao additionDao;
    private ISettleOrderDao orderDao;
    private IPurchaseContractDao contractDao;
    IPurchaseReceiptDao receiptDao;

    @Autowired
    public void setReceiptDao(IPurchaseReceiptDao receiptDao) {
        this.receiptDao = receiptDao;
    }
}
